using System;
using System.Text;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.Practices.EnterpriseLibrary.Data;
using System.Xml;
using System.IO;
using System.Xml.XPath;
using Instedd.Labs.Forms.Library;
using System.Data;
using System.Data.Common;
using Instedd.Labs.Geo.Library.Kml;

namespace Instedd.Forms.Library.Tests
{
    /// <summary>
    /// Summary description for UnitTest1
    /// </summary>
    [TestClass]
    public class SurveyLocationResolverFixture
    {
        static Database db;

        [ClassInitialize()]
        public static void ClassInitialize(TestContext testContext)
        {
            db = DatabaseFactory.CreateDatabase("FormsDatabase");
        }

        [TestMethod]
        public void CompletePostalAddressFoundUpdatesTheSurveyLocation()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            Dictionary<string, Address> surveys = new Dictionary<string, Address>();
            surveys.Add("locres01", new Address("4040 54th st", "Redmond", "WA", "4444"));

            resolver.Resolve(surveys);

            LatLong location = GetSurveyLocation("locres01");
            Assert.AreEqual(12.22, location.Lat);
            Assert.AreEqual(44.55, location.Long);
        }

        [TestMethod]
        public void StreetAndCityAndStatePostalAddressFoundUpdatesTheSurveyLocation()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            Dictionary<string, Address> surveys = new Dictionary<string, Address>();
            surveys.Add("locres01", new Address("4040 54th st", "Redmond", "WA", null));

            resolver.Resolve(surveys);

            LatLong location = GetSurveyLocation("locres01");
            Assert.AreEqual(12.23, location.Lat);
            Assert.AreEqual(44.56, location.Long);
        }

        [TestMethod]
        public void StreetAndCityPostalAddressFoundUpdatesTheSurveyLocation()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            Dictionary<string, Address> surveys = new Dictionary<string, Address>();
            surveys.Add("locres01", new Address("4040 54th st", "Redmond", null, null));

            resolver.Resolve(surveys);

            LatLong location = GetSurveyLocation("locres01");
            Assert.AreEqual(12.24, location.Lat);
            Assert.AreEqual(44.57, location.Long);
        }

        [TestMethod]
        public void StreetPostalAddressFoundUpdatesTheSurveyLocation()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            Dictionary<string, Address> surveys = new Dictionary<string, Address>();
            surveys.Add("locres01", new Address("4040 54th st", null, null, null));

            resolver.Resolve(surveys);

            LatLong location = GetSurveyLocation("locres01");
            Assert.AreEqual(12.25, location.Lat);
            Assert.AreEqual(44.58, location.Long);
        }

        [TestMethod]
        public void CompletePostalAddressFoundIncrementsResolutionAttempts()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            Dictionary<string, Address> surveys = new Dictionary<string, Address>();
            surveys.Add("locres02", new Address("4040 54th st", "Redmond", "WA", "4444"));

            resolver.Resolve(surveys);

            Assert.AreEqual(1, GetLocationResolutionAttempts("locres02"));
        }

        [TestMethod]
        public void CompletePostalAddressFoundIncre()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            Dictionary<string, Address> surveys = new Dictionary<string, Address>();
            surveys.Add("locres05", new Address(null, null, null, null));

            resolver.Resolve(surveys);

            Assert.IsNull(GetSurveyLocation("locres05"));
            Assert.AreEqual(1, GetLocationResolutionAttempts("locres05"));
        }

        [TestMethod]
        public void LocatedSurveyIsNotProcessed()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            Dictionary<string, Address> surveys = resolver.GetUnlocatedSurveys();

            Assert.IsFalse(surveys.ContainsKey("locres04"));
        }

        [TestMethod]
        public void SurveyWithAtLeastOneLocationResolutionAttempIsNotProcessed()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            Dictionary<string, Address> surveys = resolver.GetUnlocatedSurveys();

            Assert.IsFalse(surveys.ContainsKey("locres03"));
        }

        [TestMethod]
        public void ResetLocationResolutionAttemptsSetsTheCeroAttemptsValueIfTheLatLongValuesAreNull()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            resolver.ResetLocationResolutionAttemps("locres09");

            Assert.AreEqual(0, GetLocationResolutionAttempts("locres09"));
        }

        [TestMethod]
        public void ResetLocationResolutionAttemptsNotChangeTheAttemptsValueIfTheLatLongValuesAreNotNull()
        {
            SurveyLocationResolver resolver = new SurveyLocationResolver(db, new MockGeoCoder());

            resolver.ResetLocationResolutionAttemps("locres10");

            Assert.AreEqual(1, GetLocationResolutionAttempts("locres10"));
        }

        const string GetSurveyByIdSQL = "SELECT [latitude], [longitude] FROM [Forms] WHERE [id] = @id";

        private LatLong GetSurveyLocation(string surveyId)
        {
            using (DbCommand command = db.GetSqlStringCommand(GetSurveyByIdSQL))
            {
                db.AddInParameter(command, "id", DbType.String, surveyId);
                using (IDataReader reader = db.ExecuteReader(command))
                {
                    if (reader.Read())
                    {
                        try
                        {
                            return new LatLong((double)reader["latitude"], (double)reader["longitude"]);
                        }
                        catch 
                        {
                            return null;
                        }
                    }

                    return null;
                }
            }
        }

        const string GetLocationResolutionAttemptsIdSQL = "SELECT [locationResolutionAttempts] FROM [Forms] WHERE [id] = @id";

        private int? GetLocationResolutionAttempts(string surveyId)
        {
            using (DbCommand command = db.GetSqlStringCommand(GetLocationResolutionAttemptsIdSQL))
            {
                db.AddInParameter(command, "id", DbType.String, surveyId);
                using (IDataReader reader = db.ExecuteReader(command))
                {
                    if (reader.Read())
                    {
                        return (int)reader["locationResolutionAttempts"];
                    }

                    return null;
                }
            }
        }
    }
}
